package org.wheel.Service;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class XmlSqlBuilder {
    private final ExpressionParser parser = new SpelExpressionParser();
    private final StandardEvaluationContext ctx = new StandardEvaluationContext();

    /** 入口：传入 xml 路径、statementId、参数map */
    public SqlMeta build(String xmlPath, String statementId, Map<String, Object> param) {
        ctx.setVariables(param);
//        ctx.setRootObject(param);
        try {
            Document doc = DocumentBuilderFactory.newInstance()
                    .newDocumentBuilder()
                    .parse(XmlSqlBuilder.class.getResourceAsStream(xmlPath));
            NodeList list = doc.getElementsByTagName("sql");
            for (int i = 0; i < list.getLength(); i++) {
                Element e = (Element) list.item(i);
                if (statementId.equals(e.getAttribute("id"))) {
                    StringBuilder sql = new StringBuilder();
                    List<Object> args = new ArrayList<>();
                    walk(e, sql, args);
                    return new SqlMeta(sql.toString(), args);
                }
            }
            throw new IllegalArgumentException("statementId " + statementId + " not found");
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    public SqlMeta build(String sql, Map<String, Object> param) {
        try {
            ctx.setVariables(param);
            InputStream inputStream = new ByteArrayInputStream(sql.getBytes(StandardCharsets.UTF_8));
            Document doc = DocumentBuilderFactory.newInstance()
                    .newDocumentBuilder()
                    .parse(inputStream);
            Element e = doc.getDocumentElement();

            StringBuilder sqlBuilder = new StringBuilder();
            List<Object> args = new ArrayList<>();
            walk(e, sqlBuilder, args);
            return new SqlMeta(sqlBuilder.toString(), args);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    private void walk(Node node, StringBuilder sql, List<Object> args) {
        NodeList children = node.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            Node n = children.item(i);
            if (n.getNodeType() == Node.TEXT_NODE) {
                String text = n.getTextContent().replaceAll("\\s+", " ").trim();
                if (!text.isEmpty()) sql.append(text).append(" ");
            } else if (n.getNodeType() == Node.ELEMENT_NODE) {
                String tag = n.getNodeName();
                if ("if".equals(tag)) {
                    String test = ((Element) n).getAttribute("test");
                    if (Boolean.TRUE.equals(parser.parseExpression(test).getValue(ctx))) {
                        walk(n, sql, args);
                    }
                } else if ("foreach".equals(tag)) {
                    foreachNode((Element) n, sql, args);
                } else if ("where".equals(tag)) {
                    StringBuilder sub = new StringBuilder();
                    walk(n, sub, args);
                    String s = sub.toString().trim();
                    if (!s.isEmpty()) {
                        sql.append(" WHERE ");
                        sql.append(s.replaceFirst("^(AND|OR)\\s+", ""));
                    }
                } else {
                    walk(n, sql, args); // 其他标签直接递归
                }
            }
        }
    }

    private void foreachNode(Element ele, StringBuilder sql, List<Object> args) {
        String collection = ele.getAttribute("collection").replaceAll("#", "");
        String item = ele.getAttribute("item");
        String open = ele.getAttribute("open");
        String close = ele.getAttribute("close");
        String sep = ele.getAttribute("separator");
        Object colObj = ctx.lookupVariable(collection);
        if (colObj == null) return;
        @SuppressWarnings("unchecked")
        Iterable<Object> it = (Iterable<Object>) colObj;
        StringBuilder sub = new StringBuilder();
        int idx = 0;
        for (Object o : it) {
            if (idx++ > 0 && !sep.isEmpty()) sub.append(sep);
            // 把 #{item} 占位替换成 ?
//            String piece = ele.getTextContent().replaceAll("#\\{" + item + "\\}", "?");
            String key = item+idx;
            String piece = ele.getTextContent().replaceAll("#\\{" + item + "\\}", "#\\{" + key + "\\}");
            sub.append(piece.trim());
            args.add(o);               // 记录参数
        }
        if (!open.isEmpty()) sql.append(open);
        sql.append(sub);
        if (!close.isEmpty()) sql.append(close);
        sql.append(" ");
    }

    /** 返回对象：SQL + 参数列表 */
    public static class SqlMeta {
        public final String sql;
        public final List<Object> args;
        SqlMeta(String s, List<Object> a) { sql = s; args = a; }
    }

    /* ------------- 测试 ------------- */
    public static void main(String[] args) {
        Map<String, Object> param = Map.of(
                "name", "Tom",
                "minAge", 20,
                "statusList", List.of("ACTIVE", "LOCKED"));
//        String xmlPath = "/home/lanjf2000/git/web-module/plugins-jdbc-pool-spring-boot-starter/src/test/resources/mappers/TUserDaoI.xml";
        String xmlPath ="/mappers/user.xml";
        SqlMeta sm = new XmlSqlBuilder()
                .build(xmlPath, "selectUser", param);
        System.out.println("SQL  = " + sm.sql);
        System.out.println("Args = " + sm.args);
    }
}